//
// Created by Administrator on 2023/3/18.
//

#include <fcntl.h>
#include "unistd.h"
#include "response.h"
#include "sys/mman.h"
#include "sys/stat.h"
#include "log/logger_main.h"
#include "setting/local_setting.h"
#include "log/logger_main.h"
static off_t get_file_size(std::string &path)
{
    struct stat buf;

    int ret =stat(path.c_str(),&buf);
    if (ret==-1){

        LOG_INFO("获取文件大小失败{}", strerror(errno));
        return -1;
    }

    return buf.st_size;

}
std::string response::state_to_string(status_type type) {
    switch (type) {
        case ok:
            return reply::ok;
        case created:
            return reply::created;
        case accepted:
            return reply::accepted;
        case no_content:
            return reply::no_content;
        case multiple_choices:
            return reply::multiple_choices;
        case moved_permanently:
            return reply::moved_permanently;
        case moved_temporarily:
            return reply::moved_temporarily;
        case not_modified:
            return reply::not_modified;
        case bad_request:
            return reply::bad_request;
        case unauthorized:
            return reply::unauthorized;
        case forbidden:
            return reply::forbidden;
        case not_found:
            return reply::not_found;
        case internal_server_error:
            return reply::internal_server_error;
        case not_implemented:
            return reply::not_implemented;
        case bad_gateway:
            return reply::bad_gateway;
        case service_unavailable:
            return reply::service_unavailable;
        default:
            return reply::internal_server_error;
            // return std::string();
    }

}

static std::string GetGmtTime() {
    std::string szGmtTime;

    time_t rawTime;
    struct tm *timeInfo;
    char szTemp[30] = {0};
    time(&rawTime);
    timeInfo = gmtime(&rawTime);
    timeInfo->tm_year += 1;
    strftime(szTemp, sizeof(szTemp), "%a, %d %b %Y %H:%M:%S GMT", timeInfo);
    szGmtTime += (szTemp);
    return szGmtTime;
}

void response::make_response_head(int num) {
    std::string head;
    //添加头
    head += "HTTP/1.1 " + std::to_string(num) + " OK \r\n";
    if (this->req_.request_result.keepAlive) {
        //添加请求行
        head += "Connection: keep-alive\r\n";//keep-alive
    } else {
        head += "Connection: close\r\n";//keep-alive
    }

    head += "Content-type: " + this->file_type + ";charset=UTF-8\r\n";
    head += "ETag:" + this->get_file_etag() + "\r\n";//缓存
    //  head+="Expires:"+GetGmtTime()+"\r\n";
    this->buffer_.append(head);
    //Content-length 没有添加空行也没添加
    //return head;
}

std::string response::get_file_type_init() {
   // this->req_
    auto idx = this->req_.request_result.uri.find_last_of('.');
    if (idx == std::string::npos){
         this->file_type="text/html";
        return "text/html";
    }
    auto suffix = this->req_.request_result.uri.substr(idx);
    if (reply::response_suffix_type.find(suffix)!=reply::response_suffix_type.end())
    {
        this->file_type=reply::response_suffix_type.find(suffix)->second;;
        return reply::response_suffix_type.find(suffix)->second;
    }
    this->file_type="text/plain";
    return "text/plain";
}

response::response(http_request &req, std::string &buffer_) : req_(req), buffer_(buffer_) {
}

void response::make_response_body() {
        std::string  body;
        LOG_INFO("真实路径==>{}",this->real_path);
        int fd = open(this->real_path.c_str(),O_RDONLY);
        if(fd < 0){
            body = "404 ERROR FILE OPEN ERROR \r\n";
        }
        else{
            char* ptr = (char*)mmap(nullptr, get_file_size(this->real_path),PROT_READ,MAP_SHARED,fd,0);
            if(ptr==MAP_FAILED||ptr== nullptr){
                body = "500 Error FILE OPEN ERROR \r\n";
            }
            else if (ptr!= nullptr){
                auto size=get_file_size(this->real_path);
                if (size<0)
                {
                    body="404 ERROR FILE OPEN ERROR \r\n";
                    munmap(ptr, size);
                    close(fd);
                    return;
                }

                body.reserve(size);
                body.insert(body.end(),ptr,ptr + size);
                munmap(ptr,size);
            }

            close(fd);
        }
        //拼接长度
        std::string len= "Content-Length: "+std::to_string(body.size())+"\r\n" ;
        len+="\r\n";//空行之后是body内容
        this->buffer_.append(len); //添加包长度一些内容
        this->buffer_.append(body);//添加body内容
        this->buffer_.append("\r\n");//添加结束标记

}

void response::make_response() {
    if (!flag) {
        must_init();
        auto etag = std::move(get_file_etag());
        if (!this->request_etag.empty() && !etag.empty() && this->request_etag.compare(etag) == 0) {

//            std::string sufix=this->real_path.substr(this->real_path.find_last_of("."));
//            LOG_INFO("后缀{}",sufix);
            make_response_head(304);//直接返回缓存
            this->buffer_.append("\r\n");
            flag = true;
            LOG_INFO("======================缓存===========================");
            return;

        }

        make_response_head();
        make_response_body();//顺序不能颠倒
        flag = true;
    }


}

void response::must_init() {

    this->real_path = local_setting::get_instance().getPath();//设置路径
    if (req_.request_result.uri.empty()) {
        this->real_path.append("/index.html");
    } else {
        this->real_path.append(this->req_.request_result.uri);
    }
    LOG_INFO(this->real_path);
    get_file_type_init();//只是初始化
    this->request_etag = std::move(this->req_.get_etag());

    this->req_.reset();//重置链接
}

void response::reset() {

    this->real_path.clear();
    this->buffer_.clear();
    this->file_type.clear();
    this->flag = false;

}

void response::make_bad_response() {
    make_response_head();
    //拼接长度
    std::string len = "Content-Length: " + std::to_string(std::strlen(reply::bad_request)) + "\r\n";
    len += "\r\n";//空行之后是body内容
    this->buffer_.append(len); //添加包长度一些内容
    this->buffer_.append(reply::bad_request);//添加body内容
    this->buffer_.append("\r\n");//添加结束标记
}

std::string response::get_file_etag() {

    //etag 生成 文件大小的16进制+修改时间
    struct stat buf;

    int ret = stat(this->real_path.c_str(), &buf);
    if (ret == -1) {

        LOG_INFO("获取文件大小失败{}", strerror(errno));
        return "";
    }

    auto size = buf.st_size;
    auto ltime = buf.st_mtim.tv_nsec;//获取修改过时间

    return std::to_string(size) + std::to_string(ltime);
}

